/**
  ******************************************************************************
  * @file     
  * @author   古么宁
  * @brief    iec10x 协议栈链路层相关接口实现
  * @date     2019-04-10
  ******************************************************************************
  *
  * COPYRIGHT(c) 2019 GoodMorning
  *
  ******************************************************************************
  */
#ifndef _IEC10x_LINK_H_
#define _IEC10x_LINK_H_

/* Global  types ------------------------------------------------------------*/


#if (IEC_ENDIAN == IEC_LITTLE_ENDIAN)

struct master_linkctrl {
	uint8_t FUNCTION : 4 ; //功能码
	uint8_t FCV : 1 ;//Frame Count Bit Valid;0=表示帧计数位 FCB 的变化无效。1=表示帧计数位 FCB 的变化有效 。
	uint8_t FCB : 1 ;//Frame Count Bit 0-1=用于每个站连续的Send/Confirm 或者 Request/Respond 服务的变化位 
	uint8_t PRM : 1; //Primary Message,0=报文从被控站（响应站）发出,1=报文从源站（启动站） 发出
	uint8_t RES : 1 ; //保留位
};
struct slave_linkctrl {
	uint8_t FUNCTION : 4 ; //功能码
	uint8_t DFC : 1 ;//Data Flow Control,0=表示可 以接收更多后续报文。1=表示更多的后续报文将引起数据溢出
	uint8_t ACD : 1;//Access Demand;可提供两种级别的报文数据，名为 1 级数据和 2 级数据。0=没有 1 级数据传输的访问要求。1=有 1 级数据传输的访问要求。
	uint8_t PRM : 1; //Primary Message,0=报文从被控站（响应站 ）发出,1=报文从源站（启动站） 发出
	uint8_t RES : 1 ; //保留位
};

#else //大端 CPU 
struct slave_linkctrl{
	uint8_t RES : 1 ; //保留位
	uint8_t PRM : 1; 
	uint8_t ACD : 1;
	uint8_t DFC : 1 ;
	uint8_t FUNCTION : 4 ; //功能码
};
struct master_linkctrl{
	uint8_t RES : 1 ; //保留位
	uint8_t PRM : 1; 
	uint8_t FCB : 1 ;
	uint8_t FCV : 1 ;
	uint8_t FUNCTION : 4 ; //功能码
};
#endif

/// 链路控制码
typedef union _linkctrl { 
	struct master_linkctrl master; ///< 主机控制码 @see master_fncode
	struct slave_linkctrl  slave ; ///< 从机控制码 @see slave_fncode
	unsigned char          val   ;
}
linkctrl_t;


/**
 * @brief  不定长数据包帧头结构格式 
*/
struct longpkt{
	unsigned char head        ;
	unsigned char len         ;  
	unsigned char len2        ;
	unsigned char head2       ;
	unsigned char linkctrl    ;
	unsigned char linkaddr[1] ; //连接地址，有可能为 1 或 2 个字节
};

enum master_fncode {
	MASTER_RST_LNK = 0,  ///< 复位远方链路
	MASTER_RST_PRO    ,  ///< 复位运动终端的用户进程
	MASTER_TEST_LNK   ,  ///< 用于平衡传输过程测试链路功能
	MASTER_TRANS      ,  ///< 传送数据
	MASTER_TRANS_NOACK,  ///< 传送数据，无回答帧
	MASTER_LNK_STAT = 9, ///< 召唤链路状态
	MASTER_DATA_1  ,     ///< 召唤 1 级数据
	MASTER_DATA_2  ,     ///< 召唤 2 级数据
};

enum slave_fncode{
	SLAVE_OK = 0 ,   ///< 确认
	SLAVE_BUSY ,     ///< 链路忙，未接收到报文
	SLAVE_ACK = 8,   ///< 以数据响应请求帧
	SLAVE_NOACK = 9 ,///< 无所召唤数据
	SLAVE_STATE = 11 ///< 以链路状态或访问请求回答请求帧
};


typedef void  (*ASDUprofn_t)(iec10x_t * bus , uint8_t * ASDU, int ASDUlen);


/* Global  macro ------------------------------------------------------------*/

#define LISTEN_MAP_SIZE  255
/**
  * @brief    对 ASDU 数据类型进行响应。仅能通过 iec101_type_listen() 添加响应类型
  * @param    type  : ASDU 的数据类型
  * @return   返回回复码
*/
#if (LISTEN_MAP_SIZE > 128)
	#define ASDU_pro(at) (lnhashmap[0xff & (at)])
#else
	#define ASDU_pro(at) (lnhashmap[0x7f & (at)])
#endif

/// 主站控制码组成
#define MSTCTRL(FN,FCV,FCB)\
	(0x40|((FN)&0x0F)| (((FCV)&0x01)<<4) |(((FCB)&0x01)<<5))

/// 子站控制码组成
#define SLVCTRL(FN,DFC,ACD) \
	(((FN)&0x0F)| (((DFC)&0x01)<<4) |(((ACD)&0x01)<<5))


/// 平衡模式，控制域 
/// DIR：方向标志位，协商确定，一般由子到主为1
/// PRM: 启动标志位，为 1 表示由启动站向从动站传输
#define IEC_CTRL(DIR,PRM,FCB,FCV,FN) \
  (((DIR)<<7)|((PRM)<<6)|((FCB)<<5)|((FCV)<<4)|(FN))

/* Global  variables --------------------------------------------------------*/

extern ASDUprofn_t lnhashmap[] ;

/* Global  function prototypes ----------------------------------------------*/

/**
  * @brief    总线对需要发送的 ASDU 数据进行打包，调用硬件发送
  * @param    bus   : 需要解析的总线结构体 
  * @param    data  : ASDU 数据 
  * @param    len   : ASDU 数据长度 
  * @param    lnkctrl   : ASDU 数据所需控制域
  * @return   don't care
*/
void iec101_protocol_send(iec10x_t * bus , uint8_t * ASDU ,uint16_t len ,uint8_t lnkctrl);

/**
  * @brief    总线接收数据并解析
  * @param    bus   : 需要解析的总线结构体 
  * @param    data  : 包数据 
  * @param    len   : 包数据长度
  * @return   don't care
*/
void iec101_protocol_recv(iec10x_t * bus , uint8_t * data ,int len);


/**
  * @author   古么宁
  * @brief    发送重连接请求
  * @param    bus : 需要连接的总线
  * @return   
*/
void iec101mst_conncet(iec10x_t * bus) ;


/**
  * @author   古么宁
  * @brief    断开连接
  * @param    bus : 对象总线
  * @return   
*/
void iec101_disconnect(iec10x_t * bus);


/**
  * @author   古么宁
  * @brief    发送链路测试心跳包
  * @param    bus : 对象总线
  * @return   
*/
void iec101_heartbeat(iec10x_t * bus);
void iec101_connected(iec10x_t * bus);



/**
  * @brief    总线对需要发送的 ASDU 数据进行打包，调用硬件发送
  * @param    bus       : 需要解析的总线结构体 
  * @param    ASDU      : ASDU 数据 
  * @param    ASDUlen   : ASDU 数据长度 
  * @param    lnkctrl   : ASDU 数据所需控制域
  * @return   don't care
*/
void iec104_protocol_send(iec10x_t * bus , uint8_t * ASDU ,int ASDUlen ,uint8_t ctrl) ;


/**
  * @brief    总线接收数据并解析
  * @param    bus   : 需要解析的总线结构体 
  * @param    data  : 包数据 
  * @param    len   : 包数据长度
  * @return   don't care
*/
void iec104_protocol_recv(iec10x_t * bus ,const uint8_t * data ,int len) ;

/**
  * @author   古么宁
  * @brief    发送重连接请求
  * @param    bus : 需要连接的总线
  * @return   
*/
void iec104mst_conncet(iec10x_t * bus);


/**
  * @author   古么宁
  * @brief    断开连接
  * @param    bus : 对象总线
  * @return   
*/
void iec104_disconnect(iec10x_t * bus);


/**
  * @author   古么宁
  * @brief    发送链路测试心跳包
  * @param    bus : 对象总线
  * @return   
*/
void iec104_heartbeat(iec10x_t *bus) ;

#endif
